/* -*- c++ -*-
   convert.cc - LLVM-IR to GCC conversion.
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* Conversion code for LLVM IR. Translates LLVM object (modules,
   functions, etc) to GCC representation.  */

#include "llvmir.h"
#include "parser.h"

#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalValue.h"
#include "llvm/InlineAsm.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/Instruction.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/DebugLoc.h"
#include "llvm/Support/raw_os_ostream.h"

namespace llvmir
{
  /*
   * Constant values.
   */

  /* The prefix to apply to all generated names. */
  const std::string llvmir_name_prefix = "lir_";

  /*
   * Tree utility functions.
   */
  namespace Tree
  {
    /* Generate an identifier tree. */
    tree 
    make_name_tree(const std::string& name, bool artificial)
    {
      tree name_tree = NULL_TREE;

      if (artificial)
        {
          std::string full_prefix = llvmir_name_prefix + name;
          name_tree = create_tmp_var_name(full_prefix.c_str());
        }
      else
        name_tree = get_identifier(name.c_str());

      return name_tree;
    }

    /* Build and declare a label. */
    tree 
    declare_label_tree(location_t loc,
                       tree label_name,
                       tree context,
                       bool artificial)
    {
      tree label_tree = NULL;

      label_tree = build_decl(loc, LABEL_DECL, label_name, NULL_TREE);
      if (context)
        DECL_CONTEXT(label_tree) = context;
      if (artificial)
        DECL_ARTIFICIAL(label_tree) = 1;

      return label_tree;
    }

    /* Build a variable declaration */
    tree build_var_decl(location_t loc,
                        const std::string& name,
                        tree var_type,
                        bool artificial)
    {
      tree var_tree = build_decl(loc, VAR_DECL, NULL_TREE, var_type);

      if (artificial)
        DECL_ARTIFICIAL(var_tree) = 1;

      DECL_NAME(var_tree) = make_name_tree(name.c_str(), artificial);

      return var_tree;
    }
                        
    /* Add a variable declaration to a block scope. */
    void declare_var(tree new_var,
                     tree context,
                     tree block)
    {
      if (!new_var)
        return;
      gcc_assert(TREE_CODE(new_var) == VAR_DECL);

      // Add the new variable to the list of locals for this function.
      DECL_CONTEXT(new_var) = context;
      if (block)
        BLOCK_VARS(block) = chainon(BLOCK_VARS(block), new_var);
    }

    /* Make a boolean value. */
    tree make_bool_tree(bool val)
    {
      if (val)
        return boolean_true_node;
      else
        return boolean_false_node;
    }

    /* Make a string, with a given type.  */
    tree build_string(tree str_type, int length, const char* str)
    {
      tree node = ::build_string(length, str);
      TREE_TYPE(node) = str_type;
      return node;
    }

    /*
      Array operations.
     */

    /* Make an array type with an arbitrary range. */
    tree 
    make_array_type(tree elem_type, tree range)
    {
      tree array_type = NULL_TREE;

      array_type = build_array_type(elem_type, range);

      return array_type;
    }

    /* Make an array type with an integer range */
    tree 
    make_array_type_cst(tree elem_type, size_t num_elems)
    {
      tree num_elems_tree = build_int_cst_type(integer_type_node, num_elems);
      tree range = build_range_type(sizetype,
                                    copy_node(size_zero_node),
                                    num_elems_tree);
      return make_array_type(elem_type, range);
    }

    /* Make an array reference. */
    tree
    make_array_ref(location_t loc, tree array, tree index)
    {
      gcc_assert(array);
      gcc_assert(TREE_TYPE(array) 
                 && TREE_CODE(TREE_TYPE(array)) == ARRAY_TYPE);
      tree elem_type = TREE_TYPE(TREE_TYPE(array));
      tree array_ref = NULL_TREE;

      array_ref = build4_loc(loc, 
                             ARRAY_REF, elem_type,
                             array, index,
                             NULL_TREE,NULL_TREE);

      return array_ref;
    }

    /* Make an array reference with a constant index. */
    tree
    make_array_ref_cst(location_t loc, tree array, unsigned elt)
    {
      tree index = build_int_cst_type(integer_type_node, elt);
      return make_array_ref(loc, array, index);
    }

    /* 
       Vector operations.

       GCC support for vectors (of type VECTOR_TYPE) is not strong so
       vectors are represented as single dimensioned arrays.
    */

    /* Test whether a type is LLVMIR's idea of a vector type. */
    bool is_vector_type(tree type)
    {
      return TREE_CODE(type) == ARRAY_TYPE;
    }

    /* Test whether a value has LLVMIR's idea of a vector type. */
    bool has_vector_type(tree expr)
    {
      return is_vector_type(TREE_TYPE(expr));
    }

    /* Get the element type from LLVMIR's idea of a vector type. */
    bool vector_base_type(tree vec_type)
    {
      gcc_assert(is_vector_type(vec_type));
      return TREE_TYPE(vec_type);
    }

    /* Make an vector type with an arbitrary range. */
    tree 
    make_vector_type(tree elem_type, tree range)
    {
      return make_array_type(elem_type, range);
    }

    /* Make an vector type with an integer range */
    tree 
    make_vector_type_cst(tree elem_type, size_t num_elems)
    {
      return make_array_type_cst(elem_type, num_elems);
    }

    /* Make an vector reference. */
    tree
    make_vector_ref(location_t loc, tree vector, tree index)
    {
      return make_array_ref(loc, vector, index);
    }

    /* Make an vector reference with a constant index. */
    tree
    make_vector_ref_cst(location_t loc, tree vector, unsigned elt)
    {
      return make_array_ref_cst(loc, vector, elt);
    }

    /* Construct a vector by applying a mapper. */
    tree make_mapped_vector(tree base_type,
                            const unsigned size,
                            VecMapper* mapper)
    {
      gcc_assert(base_type);
      gcc_assert(mapper);
      gcc_assert(size);
      tree vector_type = make_vector_type_cst(base_type, size);
      tree vector_tree = NULL_TREE;
      VEC(constructor_elt,gc) *element_seq = 0;

      // Build the list of expressions:
      // CONSTRUCTOR [| <vector_type> |]
      // {
      //   ..
      //     mapper->build(idx)
      //   ...
      // }
      for (unsigned idx = 0; idx < size; idx += 1)
        {
          tree idx_tree = build_int_cst_type(integer_type_node, idx);
          tree elem_tree = mapper->build(idx);

          gcc_assert(elem_tree);
          // Add to the sequence of elements.
          CONSTRUCTOR_APPEND_ELT(element_seq, 
                                 idx_tree,
                                 elem_tree);
        }

      // Build the constructor.
      vector_tree = build_constructor(vector_type, element_seq);
      return vector_tree;
    }

    /* VecMapper methods. */
    VecMapper::~VecMapper()
    { /* skip */ }


    /* VecMapConst methods. */
    tree VecMapConst::build(const unsigned idx ATTRIBUTE_UNUSED)
    {
      return value;
    }

    /* VecMapUnOp methods. */
    tree VecMapUnOp::build(const unsigned idx)
    {
      tree element_tree = NULL_TREE;
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      tree expr1_ref_tree = Tree::make_vector_ref(loc, expr1, idx_tree);

      element_tree = fold_build1_loc(loc, opcode, type, expr1_ref_tree);
      return element_tree;
    }

    /* VecMapBinOp methods. */

    // Construct the true result
    tree VecMapBinOp::true_result(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      return Tree::make_vector_ref(loc, expr1, idx_tree);
    }

    // Construct the false result
    tree VecMapBinOp::false_result(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      return Tree::make_vector_ref(loc, expr2, idx_tree);
    }

    tree VecMapBinOp::build(const unsigned idx)
    {
      tree element_tree = NULL_TREE;
      tree expr1_ref_tree = true_result(idx);
      tree expr2_ref_tree = false_result(idx);

      element_tree = fold_build2_loc(loc, opcode, type,
                                     expr1_ref_tree,
                                     expr2_ref_tree);
      return element_tree;
    }

    /* VecMapPred methods */

    // Construct the predicate.
    tree VecMapPred::pred(const unsigned idx ATTRIBUTE_UNUSED)
    {
      return make_bool_tree(true);
    }

    // Construct the true result
    tree VecMapPred::true_result(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      return Tree::make_vector_ref(loc, expr1, idx_tree);
    }

    // Construct the false result
    tree VecMapPred::false_result(const unsigned idx)
    {
      tree idx_tree = build_int_cst_type(integer_type_node, idx);
      return Tree::make_vector_ref(loc, expr2, idx_tree);
    }

    // Construct the result.
    tree VecMapPred::build(unsigned idx)
    {
      tree element_tree = NULL_TREE;
      tree sel_ref_tree = pred(idx);
      tree expr1_ref_tree = true_result(idx);
      tree expr2_ref_tree = false_result(idx);

      element_tree = fold_build3_loc(loc, COND_EXPR, type, sel_ref_tree,
                                     expr1_ref_tree,
                                     expr2_ref_tree);
      return element_tree;
    }

    /* 
     * Functions that appear in gcc.git but not gcc-4.6 
     */
    tree build_case_label(tree low, tree high, tree decl)
    {
      tree label_tree = NULL_TREE;

      // CASE_LABEL_EXPR changed after gcc-4.6.
      if (TREE_CODE_LENGTH(CASE_LABEL_EXPR) == 3)
        label_tree = build3(CASE_LABEL_EXPR, void_type_node,
                            NULL_TREE, NULL_TREE,
                            NULL_TREE);
      else 
        label_tree = build4(CASE_LABEL_EXPR, void_type_node,
                            NULL_TREE, NULL_TREE,
                            NULL_TREE, NULL_TREE);

      CASE_LOW(label_tree) = low;
      CASE_HIGH(label_tree) = high;
      CASE_LABEL(label_tree) = decl;

      return label_tree;
    }

    /*
     * Handling of builtin functions.
     */

    /* Get declaration of the specified built-in or NULL if not known. */
    tree builtin_decl(enum built_in_function fncode)
    {
#ifdef LLVMIR_GCC_TRUNK
      tree fndecl = builtin_decl_implicit(fncode);
      if (fndecl)
        return fndecl;
      return builtin_decl_explicit(fncode);
#else // not (LLVMIR_GCC_TRUNK)
      return built_in_decls[fncode];
#endif // LLVMIR_GCC_TRUNK
    }
  }

  /*
   * Utility functions
   */

  /* Test for LLVM representation of boolean type. */
  bool
  is_llvm_bool_type(const llvm::Type* llvm_type)
  {
    gcc_assert(llvm_type);
    return llvm_type->isIntegerTy(1);
  }

  /*
   * Locations
   */

  /* Convert an LLVM location. */
  location_t 
  Parser::convert_location(Context *context ATTRIBUTE_UNUSED,
                           const llvm::DebugLoc *llvm_location)
  {
    location_t loc = UNKNOWN_LOCATION;
    if (!llvm_location)
      return 0;

    const unsigned line = llvm_location->getLine();
    const unsigned col = llvm_location->getCol();

    // Set the current line-start and calculate the location.
    linemap_line_start(line_table, line, col);
    linemap_position_for_column(line_table, col);

    return loc;
  }

  /*
   * Assembler strings.
   */

  /* Form a GCC assembler node from LLVM information. */
  tree
  Parser::make_asm_tree(Context *context ATTRIBUTE_UNUSED,
                        tree type_tree,
                        const std::string& llvm_asm_str,
                        const 
                        llvm::InlineAsm::ConstraintInfoVector* llvm_cnstrt_vec,
                        const ValueReprSeq* arg_repr_seq,
                        tree output_var_tree)
  {
    tree asm_tree = NULL_TREE;
    tree asm_string_tree = NULL_TREE;
    tree input_list_tree = NULL_TREE;
    tree output_list_tree = NULL_TREE;
    tree clobber_list_tree = NULL_TREE;

    if (llvm_cnstrt_vec)
      {
        // Process the constraints. Assumes that the i'th output
        // constraint refers to the i'th operand of the LLVM instruction.
        unsigned output_idx = 0;
        for (llvm::InlineAsm::ConstraintInfoVector::const_iterator
               itr = llvm_cnstrt_vec->begin(),
               end = llvm_cnstrt_vec->end();
             itr != end;
             ++ itr)
          {
            const llvm::InlineAsm::ConstraintInfo* cnstrt = &(*itr);
            const llvm::InlineAsm::ConstraintCodeVector& 
              code_vec = cnstrt->Codes;
            std::string code_str;
            tree code_list_tree = NULL_TREE;

            if (code_vec.empty())
              continue;

            // The constraint strings.
            for (unsigned code_idx = 0, code_end = code_vec.size();
                 code_idx < code_end;
                 code_idx += 1)
              {
                tree cnstrt_string_tree = NULL_TREE;
                code_str = code_vec[0];
                cnstrt_string_tree =
                  Tree::build_string(void_type_node,
                                     code_str.length(),
                                     code_str.c_str());

                if (cnstrt->Type == llvm::InlineAsm::isClobber)
                  clobber_list_tree = tree_cons(NULL_TREE,
                                                cnstrt_string_tree,
                                                clobber_list_tree);
                else
                  code_list_tree = tree_cons(NULL_TREE,
                                             cnstrt_string_tree, 
                                             code_list_tree);
              }
            code_list_tree = nreverse(code_list_tree);

            // Form the tuple.
            if (cnstrt->Type == llvm::InlineAsm::isInput)
              {
                gcc_assert(arg_repr_seq && 
                           output_idx < arg_repr_seq->size());

                tree expr_tree = (*arg_repr_seq)[output_idx]->get_tree();
                output_idx += 1;
                output_list_tree = tree_cons(code_list_tree, expr_tree, 
                                             output_list_tree);
              }
            else if (cnstrt->Type == llvm::InlineAsm::isOutput)
              {
                input_list_tree = tree_cons(code_list_tree, 
                                            output_var_tree,
                                            input_list_tree);
              }
            else if (cnstrt->Type != llvm::InlineAsm::isClobber)
              {
                // Constraints can only be input, output or clobber.
                gcc_unreachable();
              }
          }
      }
    output_list_tree = nreverse(output_list_tree);
    input_list_tree = nreverse(input_list_tree);
    clobber_list_tree = nreverse(clobber_list_tree);

    // Make the ASM string.
    asm_string_tree =
      Tree::build_string(void_type_node,
                         llvm_asm_str.length(),
                         llvm_asm_str.c_str());

    // Make the ASM expression.
    asm_tree = build5(ASM_EXPR, type_tree,
                      NULL_TREE,
                      NULL_TREE,
                      NULL_TREE,
                      NULL_TREE,
                      NULL_TREE);

    ASM_STRING(asm_tree) = asm_string_tree;
    ASM_OUTPUTS(asm_tree) = output_list_tree;
    ASM_INPUTS(asm_tree) = input_list_tree;
    ASM_CLOBBERS(asm_tree) = clobber_list_tree;

    return asm_tree;
  }

  /* 
   * Values
   */

  /* Convert an integer constant. */
  ValueRepr* 
  Parser::convert_constant_int(Context *context,
                               const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantInt *llvm_expr =
      static_cast<const llvm::ConstantInt*>(llvm_value);
    const unsigned gcc_bitwidth = HOST_BITS_PER_WIDE_INT;
    const unsigned max_bitwidth = gcc_bitwidth * 2;
    const llvm::APInt llvm_int = llvm_expr->getValue();
    const unsigned llvm_bitwidth = llvm_int.getBitWidth();
    // Test for LLVM representation of booleans.
    const llvm::IntegerType* llvm_type = llvm_expr->getType();
    const bool is_bool = is_llvm_bool_type(llvm_type);
    TypeRepr* type_repr = 0;
    tree type_tree = NULL_TREE;
    tree value_tree = NULL_TREE;
    ValueRepr* value_repr = 0;

    if (is_bool)
      {
        const unsigned bool_val = (llvm_expr->isZero()) ? 0 : 1;
        tree value_tree = Tree::make_bool_tree(bool_val);

        value_repr = new ValueRepr(llvm_expr, value_tree);
        curr_context->register_value_repr(value_repr);

        return value_repr;
      }

    if (llvm_bitwidth > max_bitwidth)
      warning_at(curr_context->src_loc, 0,
                 "bit-width of LLVM integer constant is too large.");

    // Build the integer constant, selecting the right precision.
    type_repr = convert_type(context, llvm_expr->getType());
    gcc_assert(type_repr);
    type_tree = type_repr->get_tree();

    // Get the high and low parts of the LLVM integer.
    unsigned HOST_WIDE_INT low_word = 0;
    HOST_WIDE_INT high_word = 0;

    // If the LLVM bitwidth is greater than the bitwidth of represented by a
    // single GCC integer object, get the high and low parts seperately.
    if (gcc_bitwidth < llvm_bitwidth)
      {
        const llvm::APInt llvm_lo_value =
          llvm_int.getLoBits(HOST_BITS_PER_WIDE_INT);    

        const llvm::APInt llvm_hi_value =
          llvm_int.getHiBits(HOST_BITS_PER_WIDE_INT);
        high_word = *(llvm_hi_value.getRawData());
      }
    else
      {
        low_word = *(llvm_int.getRawData());
      }

    // Make the integer constant.
    value_tree = build_int_cst_wide(type_tree, low_word, high_word);
    if (value_tree == NULL_TREE)
      return 0;

    value_repr = new ValueRepr(llvm_expr, value_tree);
    return curr_context->register_value_repr(value_repr);
  }

  /* Convert an FP constant (float or double). */
  ValueRepr*
  Parser::convert_constant_float(Context *context,
                                 const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantFP *llvm_expr =
      static_cast<const llvm::ConstantFP*>(llvm_value);
    const llvm::APFloat &llvm_float = llvm_expr->getValueAPF();
    llvm::Type *llvm_type = llvm_expr->getType();
    TypeRepr* type_repr = 0;
    ValueRepr* value_repr = 0;
    tree type_tree = NULL_TREE;
    tree value_tree = NULL_TREE;
    REAL_VALUE_TYPE final_val;

    // Convert the type.
    type_repr = convert_type(context, llvm_type);
    type_tree = type_repr->get_tree();

    // Convert the value by dumping to and reading from a string.
    // (It's the only way to be sure.)
    // buffer size: 
    // (sign + '0x' + (type bit-width/bits per hex digit) + sign + 'd').
    const unsigned bitwidth = llvm_type->getScalarSizeInBits();
    const unsigned num_digits = 1 + 2 + (bitwidth / 4) + 1 + 1;
    char *buffer = new char [num_digits + 1];
    buffer[num_digits] = 0;

    llvm_float.convertToHexString(buffer, num_digits, false, 
                                  llvm::APFloat::rmNearestTiesToEven);
    real_from_string(&final_val, buffer);

    delete [] buffer;

    // Build the constant. 
    value_tree = build_real(type_tree, final_val);
    if (value_tree == NULL_TREE)
      return 0;

    value_repr = new ValueRepr(llvm_expr, value_tree);
    return curr_context->register_value_repr(value_repr);
  }

  /* Converting constant zero for an aggregate type. */
  ValueRepr*
  Parser::convert_constant_zero(Context *context,
                                const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    ObjContext* const curr_context = context->curr_context;
    const llvm::Type *llvm_type = llvm_value->getType();
    gcc_assert(llvm_type);
    TypeRepr* type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;

    // Convert the type.
    type_repr = convert_type(context, llvm_type);

    gcc_assert(type_repr);
    gcc_assert(type_repr->get_tree());

    // Build the constant.
    value_tree = build_zero_cst(type_repr->get_tree());

    value_repr = new ValueRepr(llvm_value, value_tree);
    return curr_context->register_value_repr(value_repr);
  }

  /* Convert a constant string. */
  ValueRepr*
  Parser::convert_constant_string(Context *context,
                                  const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantArray>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantArray* llvm_array =
      static_cast<const llvm::ConstantArray*>(llvm_value);
    const llvm::ArrayType *llvm_array_type = llvm_array->getType();
    std::string llvm_string;
    tree value_tree = NULL_TREE;
    ValueRepr* value_repr = 0;
    TypeRepr* type_repr = 0;

    if (!llvm_array->isString())
      {
        error("not a string.");
        return 0;
      }

    type_repr = convert_type(context, llvm_array_type);
    gcc_assert(type_repr && type_repr->get_tree());

    llvm_string = llvm_array->getAsString();
    value_tree = Tree::build_string(type_repr->get_tree(),
                                    llvm_string.length(),
                                    llvm_string.c_str());
    
    gcc_assert(TREE_TYPE(value_tree));

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Convert a constant array. */
  ValueRepr*
  Parser::convert_constant_array(Context *context,
                                 const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantArray>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantArray* llvm_array =
      static_cast<const llvm::ConstantArray*>(llvm_value);
    const llvm::ArrayType *llvm_array_type = llvm_array->getType();
    gcc_assert(llvm_array_type);
    const unsigned array_size = llvm_array_type->getNumElements();
    TypeRepr* array_type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;
    VEC(constructor_elt,gc) *element_seq = 0;

    if (llvm_array->isString())
      return convert_constant_string(context, llvm_value);

    array_type_repr = convert_type(context, llvm_array_type);
    gcc_assert(array_type_repr && array_type_repr->get_tree());

    // Build the list of expressions:
    // CONSTRUCTOR [| <array_type> |]
    // {
    //   ..
    //   element(idx)
    //   ...
    // }
    for (unsigned idx = 0; idx < array_size; idx += 1)
      {
        tree idx_tree = build_int_cst_type(integer_type_node, idx);
        const llvm::Value* llvm_elem = llvm_array->getOperand(idx);
        ValueRepr* elem_repr = convert_value(context, llvm_elem);
        gcc_assert(elem_repr && elem_repr->get_tree());

        CONSTRUCTOR_APPEND_ELT(element_seq, idx_tree, elem_repr->get_tree());
      }

    // Build the array
    value_tree = build_constructor(array_type_repr->get_tree(), element_seq);
    
    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Convert a constant struct. */
  ValueRepr*
  Parser::convert_constant_struct(Context *context,
                                 const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantStruct>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantStruct* llvm_struct =
      static_cast<const llvm::ConstantStruct*>(llvm_value);
    const llvm::StructType *llvm_struct_type = llvm_struct->getType();
    gcc_assert(llvm_struct_type);
    const unsigned struct_size = llvm_struct_type->getNumElements();
    TypeRepr* struct_type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;
    VEC(constructor_elt,gc) *element_seq = 0;

    struct_type_repr = convert_type(context, llvm_struct_type);
    gcc_assert(struct_type_repr && struct_type_repr->get_tree());

    // Build the list of expressions:
    // CONSTRUCTOR [| <struct_type> |]
    // {
    //   ..
    //   element(idx)
    //   ...
    // }
    for (unsigned idx = 0; idx < struct_size; idx += 1)
      {
        tree idx_tree = build_int_cst_type(integer_type_node, idx);
        const llvm::Value* llvm_elem = llvm_struct->getOperand(idx);
        ValueRepr* elem_repr = convert_value(context, llvm_elem);
        gcc_assert(elem_repr && elem_repr->get_tree());

        CONSTRUCTOR_APPEND_ELT(element_seq, idx_tree, elem_repr->get_tree());
      }

    // Build the struct
    value_tree = build_constructor(struct_type_repr->get_tree(), element_seq);
    
    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Map for convert_constant_vector */
  class VecMapConvertConst: public Tree::VecMapper
  {
  public:
    tree* vector_seq;

    VecMapConvertConst(tree *vec_seq): vector_seq(vec_seq)
    { /* skip */  }

    ~VecMapConvertConst()
    {
      vector_seq = 0;
    }

    tree build(const unsigned idx)
    {
      return vector_seq[idx];
    }
  };

  /* Convert a constant vector. */
  ValueRepr*
  Parser::convert_constant_vector(Context *context,
                                  const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantVector>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantVector* llvm_vector =
      static_cast<const llvm::ConstantVector*>(llvm_value);
    const llvm::VectorType *llvm_vector_type = llvm_vector->getType();
    gcc_assert(llvm_vector_type);
    const unsigned vector_size = llvm_vector_type->getNumElements();
    const llvm::Type* llvm_base_type = llvm_vector_type->getElementType();
    TypeRepr* base_type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;

    base_type_repr = convert_type(context, llvm_base_type);
    gcc_assert(base_type_repr && base_type_repr->get_tree());

    // Convert the vector contents.
    {
      tree* vector_seq = vector_size ? new tree[vector_size] : 0;
      VecMapConvertConst mapper(vector_seq);

      for (unsigned idx = 0; idx < vector_size; idx += 1)
        {
          const llvm::Value* llvm_elem = llvm_vector->getOperand(idx);
          ValueRepr* elem_repr = convert_value(context, llvm_elem);
          gcc_assert(elem_repr && elem_repr->get_tree());
          vector_seq[idx] = elem_repr->get_tree();
        }

      // Make the vector.
      value_tree = Tree::make_mapped_vector(base_type_repr->get_tree(),
                                            vector_size,
                                            &mapper);
      // Clean-up.
      delete [] vector_seq;
    }

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Convert a constant null pointer. */
  ValueRepr*
  Parser::convert_constant_pointernull(Context *context,
                                       const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantPointerNull>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    ValueRepr* value_repr = 0;
    tree value_tree = null_pointer_node;

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Converting a constant block-address. */
  ValueRepr* 
  Parser::convert_constant_blockaddress(Context *context,
                                        const llvm::Value *llvm_expr)
  {
    gcc_assert(llvm_expr);
    ObjContext* const curr_context = context->curr_context;
    const llvm::BlockAddress* llvm_addr =
      static_cast<const llvm::BlockAddress*>(llvm_expr);
    const llvm::Type *llvm_type = llvm_expr->getType();
    gcc_assert(llvm_type);
    const llvm::BasicBlock* llvm_block = 
      static_cast<const llvm::BasicBlock*>(llvm_addr->getBasicBlock());
    ValueRepr* block_repr = 0;
    ValueRepr* value_repr = 0;

    // Look up the basic block (which should have been created in
    // convert_function). The tree is the expression to use to refer
    // to the block.
    block_repr = context->lookup_value(llvm_block);
    gcc_assert(block_repr && block_repr->get_tree());

    // Build the constant.
    value_repr = new ValueRepr(llvm_expr, block_repr->get_tree());
    return curr_context->register_value_repr(value_repr);
  }

  /* Convert a constant expression. */
  ValueRepr*
  Parser::convert_constant_expr(Context *context,
                                const llvm::Value *llvm_value)
  {
    /*
     * General case:
     * ----
     * A constant expression is translated by generating a new LLVM
     * instruction to calculate the value and initialising a variable
     * with the result of the instruction.
     *
     * [| <const-expr(opcode, operands)> |]
     * -->
     * [| Instruction(opcode, operands) |]
     *
     * Special cases:
     * ----
     * [| select(cond, tval, fval) |]
     * -->
     * COND_EXPR [| cond |], [| tval |], [| fval |]
     *
     * [| extractvalue(object, <indices>) |]
     * -->
     * cond_field_index([| object |], <indices>)
     *
     * [| insertvalue(object, value, <indices> |]
     * -->
     * MODIFY_EXPR(cond_field_index([| object |], <indices>),
     *             [| value |])
     *
     */
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::ConstantExpr>(*llvm_value));

    location_t locus = UNKNOWN_LOCATION;
    ObjContext* const curr_context = context->curr_context;
    const llvm::ConstantExpr* llvm_expr =
      static_cast<const llvm::ConstantExpr*>(llvm_value);
    llvm::Type *llvm_type = llvm_expr->getType();
    const unsigned llvm_opcode = llvm_expr->getOpcode();
    gcc_assert(llvm_type);
    llvm::Instruction *llvm_instr = 0;
    tree value_tree = NULL_TREE;
    ValueRepr* value_repr = 0;

    // Form the instructions
    if (llvm_expr->isCompare())
      {
        // Comparions.
        llvm::Value* llvm_lhs = llvm_expr->getOperand(0);
        llvm::Value* llvm_rhs = llvm_expr->getOperand(1);
        llvm::CmpInst::Predicate llvm_pred = 
          static_cast<llvm::CmpInst::Predicate>(llvm_expr->getPredicate());

        if (llvm::CmpInst::isIntPredicate(llvm_pred))
          llvm_instr = 
              new llvm::ICmpInst(llvm_pred, llvm_lhs, llvm_rhs);
        else
          llvm_instr = 
              new llvm::FCmpInst(llvm_pred, llvm_lhs, llvm_rhs);
      }
    else if (llvm_expr->isCast())
      {
        // Type casting
        llvm::Value* llvm_inner_value = llvm_expr->getOperand(0);
        llvm::Instruction::CastOps llvm_castop = 
          static_cast<llvm::Instruction::CastOps>(llvm_opcode);

        llvm_instr = 
          llvm::CastInst::Create(llvm_castop, llvm_inner_value, llvm_type);
      }
    else
      {
        // Everything else
        switch(llvm_opcode)
          {
            // Binary operations
          case llvm::Instruction::Add:
          case llvm::Instruction::FAdd:
          case llvm::Instruction::Sub:
          case llvm::Instruction::FSub:
          case llvm::Instruction::Mul:
          case llvm::Instruction::FMul:
          case llvm::Instruction::UDiv:
          case llvm::Instruction::SDiv:
          case llvm::Instruction::FDiv:
          case llvm::Instruction::URem:
          case llvm::Instruction::SRem:
          case llvm::Instruction::FRem:

            // Bitwise binary operations
          case llvm::Instruction::Shl:
          case llvm::Instruction::LShr:
          case llvm::Instruction::AShr:
          case llvm::Instruction::And:
          case llvm::Instruction::Or:
          case llvm::Instruction::Xor:
            {
              gcc_assert(llvm_expr->getNumOperands() == 2);

              llvm::Value* llvm_lhs = llvm_expr->getOperand(0);
              llvm::Value* llvm_rhs = llvm_expr->getOperand(1);
              llvm::Instruction::BinaryOps llvm_binaryop = 
                static_cast<llvm::Instruction::BinaryOps>(llvm_opcode);

              llvm_instr = 
                llvm::BinaryOperator::Create(llvm_binaryop,
                                             llvm_lhs, llvm_rhs);
              break;
            }

            // Vector operations
          case llvm::Instruction::ExtractElement:
            {
              llvm::Value* llvm_vec = llvm_expr->getOperand(0);
              llvm::Value* llvm_idx = llvm_expr->getOperand(1);

              llvm_instr = 
                llvm::ExtractElementInst::Create(llvm_vec, llvm_idx);
              break;
            }
          case llvm::Instruction::InsertElement:
            {
              llvm::Value* llvm_vec = llvm_expr->getOperand(0);
              llvm::Value* llvm_newelt = llvm_expr->getOperand(1);
              llvm::Value* llvm_idx = llvm_expr->getOperand(2);

              llvm_instr = 
                llvm::InsertElementInst
                ::Create(llvm_vec, llvm_newelt, llvm_idx);

              break;
            }
          case llvm::Instruction::ShuffleVector:
            {
              llvm::Value* llvm_vec1 = llvm_expr->getOperand(0);
              llvm::Value* llvm_vec2 = llvm_expr->getOperand(1);
              llvm::Value* llvm_mask = llvm_expr->getOperand(2);

              llvm_instr = 
                new llvm::ShuffleVectorInst(llvm_vec1, llvm_vec2, llvm_mask);
              break;
            }

            // Aggregate operations
          case llvm::Instruction::ExtractValue:
            {
              gcc_assert(llvm_expr->hasIndices());
              const llvm::Value* llvm_object = 
                llvm_expr->getOperand(0);
              const llvm::ArrayRef<unsigned>& llvm_indices = llvm_expr->getIndices();

              value_tree = convert_field_index(context,
                                               llvm_object,
                                               llvm_indices.size(),
                                               llvm_indices.begin(),
                                               llvm_indices.end());
              break;
            }
          case llvm::Instruction::InsertValue:
            {
              gcc_assert(llvm_expr->hasIndices());
              const llvm::Value* llvm_object = llvm_expr->getOperand(0);
              const llvm::Value* llvm_newval = llvm_expr->getOperand(1);
              const llvm::ArrayRef<unsigned>& llvm_indices = 
                llvm_expr->getIndices();
              ValueRepr* newval_repr = convert_value(context, llvm_newval);
              TypeRepr* type_repr = convert_type(context, llvm_type);
              tree field_tree = convert_field_index(context,
                                                    llvm_object,
                                                    llvm_indices.size(),
                                                    llvm_indices.begin(),
                                                    llvm_indices.end());

              value_tree = 
                fold_build2(MODIFY_EXPR, 
                            type_repr->get_tree(),
                            field_tree,
                            newval_repr->get_tree());
              break;
            }

            // Other instructions
          case llvm::Instruction::GetElementPtr:
            {
              llvm::Value* llvm_ptrval = llvm_expr->getOperand(0);
              const unsigned num_indices = llvm_expr->getNumOperands() - 1;
              llvm::SmallVector<llvm::Value*, 4> llvm_operands_seq;

              for (unsigned idx = 0;
                   idx < num_indices;
                   idx += 1)
                {
                  llvm::Value* llvm_val = llvm_expr->getOperand(idx + 1);
                  llvm_operands_seq.push_back(llvm_val);
                }

              llvm_instr = 
                llvm::GetElementPtrInst
                ::Create(llvm_ptrval, llvm_operands_seq);

              break;
            }
          case llvm::Instruction::Select:
            {
              llvm::Value* llvm_cond = llvm_expr->getOperand(0);
              llvm::Value* llvm_tval = llvm_expr->getOperand(1);
              llvm::Value* llvm_fval = llvm_expr->getOperand(2);
              ValueRepr* cond_repr = convert_value(context, llvm_cond);
              ValueRepr* tval_repr = convert_value(context, llvm_tval);
              ValueRepr* fval_repr = convert_value(context, llvm_fval);
              TypeRepr* type_repr = convert_type(context, llvm_type);

              value_tree = fold_build3_loc(locus,
                                           COND_EXPR,
                                           type_repr->get_tree(),
                                           cond_repr->get_tree(),
                                           tval_repr->get_tree(),
                                           fval_repr->get_tree());
              break;
            }

          default:
            {
              gcc_unreachable();
              break;
            }
          }    
      }
    gcc_assert(llvm_instr);

    // If an instruction is given, form the result for the constant
    // expression from the instruction tree.
    if (llvm_instr)
      {
        InstrRepr* instr_repr = convert_instruction(context, llvm_instr);
        gcc_assert(instr_repr && instr_repr->get_tree());

        value_tree = instr_repr->get_tree();

        // Mark the newly created llvm instruction as being managed by
        // its representation (so that it will be deleted when the
        // repr-object is deleted).
        gcc_assert(llvm_instr == instr_repr->get_value());
        instr_repr->set_managed(true);
      }
    else
      {
        // If an instruction isn't being used, then a tree must have
        // been constructed.
        gcc_assert(value_tree);
      }

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Convert an undefined value. */
  ValueRepr*
  Parser::convert_constant_undef(Context *context,
                                 const llvm::Value *llvm_value)
  {
    gcc_assert(llvm_value);
    gcc_assert(llvm::isa<llvm::UndefValue>(*llvm_value));

    ObjContext* const curr_context = context->curr_context;
    const llvm::Type* llvm_type = llvm_value->getType();
    TypeRepr* type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;

    type_repr = convert_type(context, llvm_type);
    gcc_assert(type_repr && type_repr->get_tree());

    // Atomic values of type ty are converted to ((ty)0).
    if (llvm_type->isSingleValueType() 
        && !llvm_type->isVectorTy())
      {
        value_tree = fold_build1(VIEW_CONVERT_EXPR, 
                                 type_repr->get_tree(),
                                 null_pointer_node);
      }
    else 
      {
        // Non-Atomic values are converted to CONSTRUCTOR_EXPR{ ty, 0}
        // (Vector value are being seperated out from arrays and structures.)
        VEC(constructor_elt,gc) *element_seq = 0;

        if (llvm_type->isVectorTy())
          value_tree = build_constructor(type_repr->get_tree(), element_seq);
        else 
          value_tree = build_constructor(type_repr->get_tree(), element_seq);
      }    

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Toplevel for converting an LLVM value. */
  ValueRepr*
  Parser::convert_value(Context *context, 
                        const llvm::Value *llvm_given_value)
  {
    gcc_assert(context && context->curr_context);
    const llvm::Value* llvm_value = llvm_given_value;
    ValueRepr* value_repr = 0;

    if (!llvm_given_value)
      return 0;

    // Chase the real value to be converted.
    for (const llvm::Value* llvm_value_itr = llvm_given_value;
         llvm_value_itr != 0;
         llvm_value_itr = context->lookup_alias(llvm_value_itr))
      {
        llvm_value = llvm_value_itr;

        // Check whether this value has a conversion.
        value_repr = context->lookup_value(llvm_value);
        if (value_repr)
          return value_repr;

        // Check whether this is an alias with a conversions.
        value_repr = context->lookup_alias_repr(llvm_value);
        if (value_repr)
          {
            // Record the conversion for the given value.
            if (llvm_value != llvm_given_value)
              context->store_alias_repr(llvm_given_value, value_repr);

            return value_repr;
          }
      }
    gcc_assert(llvm_value);

    ////
    // Program elements
    if (llvm::isa<llvm::Instruction>(*llvm_value))
      value_repr = convert_instruction(context, llvm_value);
    else if (llvm::isa<llvm::BasicBlock>(*llvm_value))
      value_repr = convert_basic_block(context, llvm_value);
    else if (llvm::isa<llvm::Function>(*llvm_value))
      value_repr = convert_function_value(context, llvm_value);
    else
    ////
    // Constants
    if (llvm::isa<llvm::ConstantInt>(*llvm_value))
      value_repr = convert_constant_int(context, llvm_value);
    else if (llvm::isa<llvm::ConstantFP>(*llvm_value))
      value_repr = convert_constant_float(context, llvm_value);
    else if (llvm::isa<llvm::ConstantAggregateZero>(*llvm_value))
      value_repr = convert_constant_zero(context, llvm_value);
    else if (llvm::isa<llvm::ConstantArray>(*llvm_value))
      value_repr = convert_constant_array(context, llvm_value);
    else if (llvm::isa<llvm::ConstantStruct>(*llvm_value))
      value_repr = convert_constant_struct(context, llvm_value);
    else if (llvm::isa<llvm::ConstantVector>(*llvm_value))
      value_repr = convert_constant_vector(context, llvm_value);
    else if (llvm::isa<llvm::ConstantPointerNull>(*llvm_value))
      value_repr = convert_constant_pointernull(context, llvm_value);
    else if (llvm::isa<llvm::BlockAddress>(*llvm_value))
      value_repr = convert_constant_blockaddress(context, llvm_value);
    else if (llvm::isa<llvm::ConstantExpr>(*llvm_value))
      value_repr = convert_constant_expr(context, llvm_value);
    else if (llvm::isa<llvm::UndefValue>(*llvm_value))
      value_repr = convert_constant_undef(context, llvm_value);
    ////
    // Variables
    else if (llvm::isa<llvm::Argument>(*llvm_value))
      value_repr = convert_parameter(context, llvm_value);
    ////
    // Meta-data
    else if (llvm::isa<llvm::MDNode>(*llvm_value))
      value_repr = convert_metadata(context, llvm_value);
    else if (llvm::isa<llvm::MDString>(*llvm_value))
      value_repr = convert_metadata(context, llvm_value);
    ////
    // Unknown objects.
    else
      gcc_unreachable();

    // Check that the value was properly added to the conversion table
    // by the function that created it.
    gcc_assert(context->lookup_value(llvm_value));

    // Record the representation for an aliased value.
    if (llvm_value != llvm_given_value)
      context->store_alias_repr(llvm_given_value, value_repr);

    return value_repr;
  }

  /*
   * Meta-data
   */

  /* Convert a meta-data object which occurs as a value. By default,
     this is mapped to an undefined value. */
  ValueRepr* 
  Parser::convert_metadata(Context *context,
                           const llvm::Value* llvm_value)
  { 
    warning(0, "metadata object is mapped to undef.");

    ObjContext* const curr_context = context->curr_context;
    const llvm::Type* llvm_type = llvm_value->getType();
    TypeRepr* type_repr = 0;
    ValueRepr* value_repr = 0;
    tree value_tree = NULL_TREE;

    type_repr = convert_type(context, llvm_type);
    gcc_assert(type_repr && type_repr->get_tree());

    value_tree = fold_build1(VIEW_CONVERT_EXPR, 
                             type_repr->get_tree(),
                             null_pointer_node);

    value_repr = new ValueRepr(llvm_value, value_tree);
    curr_context->register_value_repr(value_repr);
    return value_repr;
  }

  /* Process a module-level meta-data object. */
  void 
  Parser::process_module_metadata(Context *context ATTRIBUTE_UNUSED,
                                  const llvm::NamedMDNode* 
                                  llvm_metadata ATTRIBUTE_UNUSED)
  { 
    warning(0, "ignoring module metadata.");
  }


  /* Process a module-level assembler string. */
  void 
  Parser::process_module_asm(Context *context ATTRIBUTE_UNUSED,
                             const std::string& llvm_asm_str)
  {
    /* Pass the assembler string to the GCC middle-end. */
    tree asm_tree = Tree::build_string(void_type_node,
                                       llvm_asm_str.length(),
                                       llvm_asm_str.c_str());

    cgraph_add_asm_node(asm_tree);
  }

  /* Apply modifiers to a global variable */
  VarRepr*
  Parser::apply_variable_modifiers(Context *context ATTRIBUTE_UNUSED, 
                                   const llvm::GlobalVariable* llvm_var,
                                   VarRepr* var_repr)
  { 
    gcc_assert(var_repr && var_repr->get_decln());
    tree var_decl_tree = var_repr->get_decln();

    // Set linkage.
    const llvm::GlobalValue::LinkageTypes 
      llvm_linkage = llvm_var->getLinkage();

    switch (llvm_linkage)
      {
      case llvm::GlobalValue::ExternalLinkage:
      case llvm::GlobalValue::AvailableExternallyLinkage:
        TREE_PUBLIC(var_decl_tree) = true;
        break;
      case llvm::GlobalValue::InternalLinkage:
      case llvm::GlobalValue::PrivateLinkage:
        TREE_PUBLIC(var_decl_tree) = false;
        break;
      case llvm::GlobalValue::WeakAnyLinkage:
      case llvm::GlobalValue::WeakODRLinkage:
        DECL_WEAK(var_decl_tree) = true;
        TREE_PUBLIC(var_decl_tree) = true;
        break;
      case llvm::GlobalValue::DLLImportLinkage:
        DECL_DLLIMPORT_P(var_decl_tree) = true;
      default:
        // By default, use externally visible linkage
        TREE_PUBLIC(var_decl_tree) = true;
        break;
      }

    return var_repr;
  }

  /* Convert a global variable */
  VarRepr*
  Parser::convert_variable(Context *context, 
                           const llvm::Value *llvm_value)
  {
    gcc_assert(context
               && context->unit_context.unit_scope_tree 
               && context->unit_context.unit_decln_tree);
    gcc_assert(llvm_value);

    location_t var_loc = UNKNOWN_LOCATION;
    const llvm::GlobalVariable* llvm_var = 
      static_cast<const llvm::GlobalVariable*>(llvm_value);
    const llvm::Type* llvm_type = llvm_var->getType();
    const llvm::Value* llvm_init = 0;
    TypeRepr* type_repr = 0;
    VarRepr* var_repr = 0;
    ValueRepr* init_repr = 0;
    tree var_tree = NULL_TREE;
    tree var_ref_tree = NULL_TREE;

    // Check for a existing declaration.
    var_repr = static_cast<VarRepr*>(context->lookup_value(llvm_var));

    if (var_repr && var_repr->defn_repr 
        && llvm_var->hasInitializer())
      {
        std::string llvm_name = llvm_var->getName();

        if (llvm_var->hasInitializer())
          warning_at(var_loc, 0, "redefining global variable %s",
                     llvm_name.c_str());
      }

    if (var_repr)
      {
        // Get the declaration tree.
        gcc_assert(var_repr->get_tree());
        var_tree = var_repr->get_decln();
        gcc_assert(TREE_CODE(var_tree) == VAR_DECL);
      }
    else
      {
        // Make a declaration.
        std::string llvm_name = llvm_var->getName();

        type_repr = convert_type(context, llvm_type);
        gcc_assert(type_repr && type_repr->get_tree());

        if (llvm_var->hasInitializer())
          {
            llvm_init = llvm_var->getInitializer();

            init_repr = convert_value(context, llvm_init);
            gcc_assert(init_repr && init_repr->get_tree());
          }

        var_tree = Tree::build_var_decl(var_loc, llvm_name,
                                        type_repr->get_tree(),
                                        false);
        gcc_assert(TREE_CODE(var_tree) == VAR_DECL);

        Tree::declare_var(var_tree, 
                          context->unit_context.unit_decln_tree,
                          context->unit_context.unit_scope_tree);

        // Set the context.
        DECL_CONTEXT(var_tree) = context->unit_context.unit_decln_tree;

        TREE_USED(var_tree) = 1;
        TREE_PUBLIC(var_tree) = 1;

        if (llvm_var->hasInitializer())
          TREE_STATIC(var_tree) = 1;
        else
          DECL_EXTERNAL(var_tree) = 1;

        // Set-up the tree used to refer to the variable.
        var_ref_tree = build1(ADDR_EXPR, 
                              type_repr->get_tree(),
                              var_tree);

        var_repr = new VarRepr(llvm_var, var_ref_tree);
        context->register_value_repr(var_repr);
        var_repr->decln_tree = var_tree;

        // Apply the modifiers.
        var_repr = apply_variable_modifiers(context, llvm_var, var_repr);

        layout_decl(var_tree, 0);
      }

    // Nothing more to do for declarations.
    if (llvm_var->isDeclaration()
        || !(llvm_var->hasInitializer()))
      return var_repr;

    // Set-up the initializer.
    llvm_init = llvm_var->getInitializer();

    init_repr = convert_value(context, llvm_init);
    gcc_assert(init_repr && init_repr->get_tree());

    DECL_INITIAL(var_tree) = init_repr->get_tree();
    var_repr->defn_repr = init_repr;

    return var_repr;
  }

  /*
   * Entry-point for compiling and LLVM module.
   */

  /* Compile an LLVM module. */
  bool Parser::compile_module(Context *context,
                              const llvm::Module *llvm_module) 
  { 
    gcc_assert(context);
    gcc_assert(!(context->unit_context.unit_decln_tree));
    gcc_assert(!(context->unit_context.unit_scope_tree));

    if (!llvm_module)
      return false;

    std::string llvm_name = llvm_module->getModuleIdentifier();
    tree name_tree = NULL_TREE;
    tree unit_decl_tree = NULL_TREE;
    tree unit_scope_tree = NULL_TREE;
    ObjContext var_context;

    // Set-up the declaration and scope.
    name_tree = Tree::make_name_tree(llvm_name.c_str(), false);

    unit_decl_tree = build_translation_unit_decl(name_tree);
    unit_scope_tree = build_block(NULL_TREE, NULL_TREE,
                                  unit_decl_tree, NULL_TREE);
    DECL_INITIAL(unit_decl_tree) = unit_scope_tree;
    TREE_USED(unit_scope_tree) = true;

    // Set up the context with the current unit.
    context->unit_context.unit_decln_tree = unit_decl_tree;
    context->unit_context.unit_scope_tree = unit_scope_tree;

    // Process the module-level assembler.
    process_module_asm(context, llvm_module->getModuleInlineAsm());

    // Process the meta-data.
    for (llvm::Module::const_named_metadata_iterator
           itr = llvm_module->named_metadata_begin(),
           end = llvm_module->named_metadata_end();
         itr != end;
         ++itr)
      {
        const llvm::NamedMDNode* llvm_metadata = &(*itr);
        if (!llvm_metadata)
          continue;

        process_module_metadata(context, llvm_metadata);
      }

    // Record the value aliases.
    for (llvm::Module::const_alias_iterator
           itr = llvm_module->alias_begin(),
           end = llvm_module->alias_end();
         itr != end;
         ++itr)
      {
        const llvm::GlobalAlias* llvm_defn = &(*itr);
        if (!llvm_defn)
          continue;

        const llvm::Value* llvm_alias = llvm_defn->getAliasee();
        const llvm::Value* llvm_actual = llvm_defn->getAliasedGlobal();

        if (llvm_alias && llvm_actual)
          context->store_alias(llvm_alias, llvm_actual);
      }

    // Convert the global variables.
    gcc_assert(!context->curr_context);
    context->curr_context = &var_context;

    for (llvm::Module::const_global_iterator 
           itr = llvm_module->global_begin(),
           end = llvm_module->global_end();
         itr != end;
         ++itr)
      {
        const llvm::GlobalVariable* llvm_var = &(*itr);
        VarRepr* var_repr = 0;

        var_repr = convert_variable(context, llvm_var);
        gcc_assert(var_repr
                   && var_repr->get_tree() 
                   && var_repr->get_decln());

        if (!llvm_var->hasInitializer())
          rest_of_decl_compilation(var_repr->get_decln(), 0, 0);
        else
          {
            tree var_decl_tree = var_repr->get_decln();
            varpool_finalize_decl(var_decl_tree);
          }
      }
    context->curr_context = 0;
    var_context.reset();

    // Convert the functions. First the declarations.
    for (llvm::Module::const_iterator 
           itr = llvm_module->begin(),
           end = llvm_module->end();
         itr != end;
         ++itr)
      {
        const llvm::Function* llvm_function = &(*itr);
        FunctionRepr* fn_repr = 0;
        tree fn_decl_tree = NULL_TREE;

        // Declare the function.
        fn_repr = convert_function_decln(context, llvm_function);
        gcc_assert(fn_repr && fn_repr->get_decln());
        fn_decl_tree = fn_repr->get_decln();

        // Pass declarations to GCC
        if (!llvm_function->isDeclaration())
          continue;

        if (context->options->debug_output)
          {
            // Debugging
            std::cout << "LLVM IR --> GENERIC\n";
            llvmir::debug_print_function(fn_decl_tree);
          }
        rest_of_decl_compilation(fn_decl_tree, 0, 0);
      }

    // The function definitions.
    for (llvm::Module::const_iterator 
           itr = llvm_module->begin(),
           end = llvm_module->end();
         itr != end;
         ++itr)
      {
        const llvm::Function* llvm_function = &(*itr);
        location_t fn_location = 0;
        FunctionRepr* fn_repr = 0;
        tree fn_decl_tree = NULL_TREE;

        if (llvm_function->isDeclaration())
          continue;

        fn_repr = convert_function(context, llvm_function);
        gcc_assert(fn_repr && fn_repr->get_decln());
        fn_decl_tree = fn_repr->get_decln();

        if (context->options->debug_output)
          {
            // Debugging
            std::cout << "LLVM IR --> GENERIC\n";
            llvmir::debug_print_function(fn_decl_tree);
          }

        // Pass to GCC
        current_function_decl = fn_decl_tree;
        if (DECL_STRUCT_FUNCTION(fn_decl_tree) == NULL)
          push_struct_function(fn_decl_tree);
        else
          push_cfun(DECL_STRUCT_FUNCTION(fn_decl_tree));
    
        cfun->function_end_locus = fn_location;
        cgraph_finalize_function(fn_decl_tree, true);

        current_function_decl = NULL_TREE;
        pop_cfun();
      }

    // Reset the translation unit context.
    context->unit_context.reset();

    return true;
  }

  /* Compile an LLVM module. */
  bool Parser::compile_file(Context *context,
                            const char* filename) 
  { 
    llvm::Module* llvm_module = 0;
    bool ret = true;
    gcc_assert(context);
    gcc_assert(!(context->unit_context.unit_decln_tree));
    gcc_assert(!(context->unit_context.unit_scope_tree));

    if (strcmp(filename, "-") == 0)
      fatal_error("cannot use stdin as an input file: %m");

    // Set up the location information.
    linemap_add(line_table, LC_ENTER, 0, filename, 1);

    // Read the file to get an LLVM module.
    llvm_module = this->parse_file(context, filename);
    if (!llvm_module)
      return false;

    if (context->options->debug_output)
      llvmir::print_module(llvm_module);

    // Test for running the compilation passes.
    if (!context->options->syntax_only)
      ret = this->compile_module(context, llvm_module);

    // Delete the module.
    delete(llvm_module);
    llvm_module = 0;

    // Update the location information.
    linemap_add(line_table, LC_LEAVE, 0, NULL, 0);

    // Done
    return ret;
  }

} // namespace llvmir
